package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/gin-gonic/gin/binding"
	"github.com/go-playground/validator/v10"
	"math/rand"
	"net/http"
	"strconv"
	"time"
)

type Res struct {
	Code int64     `json:"code"`
	Aa   int       `json:"aa,omitempty"`
	Bb   int       `json:"bb,omitempty"`
	Cc   time.Time `json:"cc"`
}

func initlog(eng *gin.Engine) {
	// LoggerWithFormatter middleware will write the logs to gin.DefaultWriter
	// By default gin.DefaultWriter = os.Stdout
	eng.Use(gin.LoggerWithFormatter(func(param gin.LogFormatterParams) string {

		// your custom format
		return fmt.Sprintf("[%s] - [%s] \"%s %s %s %d %s \"%s\" %s\"\n",
			param.TimeStamp.Format(time.RFC3339),
			param.ClientIP,
			param.Method,
			param.Path,
			param.Request.Proto,
			param.StatusCode,
			param.Latency,
			param.Request.UserAgent(),
			param.ErrorMessage,
		)
	}))
	eng.Use(gin.Recovery())
}

func getBoy(ctx *gin.Context) {
	rand.Seed(time.Now().UnixNano())
	res := Res{
		Code: http.StatusOK,
		Aa:   rand.Intn(1000),
		Bb:   rand.Intn(1000),
		Cc:   time.Now(),
	}
	ctx.JSON(http.StatusOK, &res)
}

func getGirl(ctx *gin.Context) {
	ctx.Writer.Write([]byte("hi girl"))
}

//	curl --location --request GET '127.0.0.1:8080/stu?name=lll'
//	{"addr": "default get query addr","name": "lll"}
func GetQuery(engine *gin.Engine) {
	engine.GET("/stu", func(c *gin.Context) {
		name := c.Query("name")
		addr := c.DefaultQuery("addr", "default get query addr")
		c.JSON(200, gin.H{
			"name": name,
			"addr": addr,
		})
	})
}

//	curl --location --request POST '127.0.0.1:8080/home' --form 'name="lwq"'
//	name: lwq,addr: default post form addr
func PostForm(engine *gin.Engine) {
	engine.POST("/home", func(c *gin.Context) {
		name := c.PostForm("name")
		addr := c.DefaultPostForm("addr", "default post form addr")
		c.String(http.StatusOK, "name: %s,addr: %s", name, addr)
	})
}

//	curl --location --request GET '127.0.0.1:8080/user/qwer/qqq'
//	name: qwer,addr: qqq
func RestFulForm(engine *gin.Engine) {
	engine.GET("/user/:name/:addr", func(c *gin.Context) {
		name := c.Param("name")
		addr := c.Param("addr")
		c.String(http.StatusOK, "name: %s,addr: %s", name, addr)
	})
}

//	upload one file:
//	curl --location --request POST '127.0.0.1:8080/upload' \
//--form 'file=@"/Users/lwq/Desktop/new_queue_add.txt"'
//	upload success: new_queue_add.txt
func UploadFile(engine *gin.Engine) {
	engine.POST("/upload", func(c *gin.Context) {
		file, err := c.FormFile("file")
		if err != nil {
			fmt.Println(err)
			c.String(http.StatusNotFound, "upload file failed")
		} else {
			c.SaveUploadedFile(file, "./"+file.Filename)
			c.String(http.StatusOK, "upload success: "+file.Filename)
		}

	})
}

//	upload multi files:
//	curl --location --request POST '127.0.0.1:8080/multupload' \
//--form 'files=@"/Users/lwq/Desktop/3671352ac65c103876debca4a5119313b17e89d1.jpeg"' \
//--form 'files=@"/Users/lwq/Desktop/src=http___qiyipet.com_wp-content_uploads_2019_08_1435559788120080.jpg&refer=http___qiyipet.jpeg"' \
//--form 'files=@"/Users/lwq/Desktop/UNIX_SCRIPT.sh"'
//	3 upload success
func UploadFiles(engine *gin.Engine) {
	engine.POST("/multupload", func(c *gin.Context) {
		form, _ := c.MultipartForm()
		files := form.File["files"]
		for _, file := range files {
			c.SaveUploadedFile(file, "./"+file.Filename)
		}
		c.String(http.StatusOK, strconv.Itoa(len(files))+" upload success")
	})
}

type Student struct {
	Name       string    `json:"name" form:"username" xml:"name" yaml:"name" uri:"name"`
	Addr       string    `json:"addr" form:"addr" xml:"addr" yaml:"addr" uri:"addr"`
	Enrollment time.Time `form:"enroll" binding:"required,before_today" time_format:"2006-01-02" time_utc:"8"`
	Graduation time.Time `form:"graduate" binding:"required,gtfield=Enrollment" time_format:"2006-01-02" time_utc:"8"`
}

var beforeToday validator.Func = func(fl validator.FieldLevel) bool {
	if date, ok := fl.Field().Interface().(time.Time); ok {
		today := time.Now()
		if date.Before(today) {
			return true
		}
	}
	return false
}

//	curl --location --request POST '127.0.0.1:8080/stu/form' \
//--header 'Content-Type: application/x-www-form-urlencoded' \
//--data-urlencode 'username=qqq' \
//--data-urlencode 'addr=newaddr'
//	qqq live in newaddr
func FormBind(engine *gin.Engine) {
	engine.POST("/stu/form", func(ctx *gin.Context) {
		var stu Student
		if err := ctx.ShouldBind(&stu); err != nil {
			fmt.Println(err)
			ctx.String(http.StatusBadRequest, "parse param failed")
		} else {
			//ctx.String(http.StatusOK, stu.Name+" live in "+stu.Addr+" enroll "+stu.Enrollment.String()+
			//	" graduate "+stu.Graduation.String())
			ctx.JSON(http.StatusOK, &stu)
		}
	})
}

//	curl --location --request POST '127.0.0.1:8080/stu/json' \
//--header 'Content-Type: application/json' \
//--data-raw '{
//"dname":"333",
//"addr":"asdasd"
//}'
//
// 333 live in asdasd
func JsonBind(engine *gin.Engine) {
	engine.POST("/stu/json", func(ctx *gin.Context) {
		var stu Student
		if err := ctx.ShouldBindJSON(&stu); err != nil {
			fmt.Println(err)
			ctx.String(http.StatusBadRequest, "parse param failed")
		} else {
			ctx.String(http.StatusOK, stu.Name+" live in "+stu.Addr)
		}
	})
}

//	curl --location --request POST '127.0.0.1:8080/stu/xml' \
//--header 'Content-Type: application/xml' \
//--data-raw '<ind>
//<name>llll</name>
//<addr>shsh</addr>
//</ind>'
//	llll live in shsh
func XmlBind(engine *gin.Engine) {
	engine.POST("/stu/xml", func(ctx *gin.Context) {
		var stu Student
		if err := ctx.ShouldBindXML(&stu); err != nil {
			fmt.Println(err)
			ctx.String(http.StatusBadRequest, "parse param failed")
		} else {
			ctx.String(http.StatusOK, stu.Name+" live in "+stu.Addr)
		}
	})
}

//	curl --location --request POST '127.0.0.1:8080/stu/yaml' \
//--header 'Content-Type: application/xml' \
//--data-raw 'name: qqw
//addr: bj'
//	qqw live in bj
func YamlBind(engine *gin.Engine) {
	engine.POST("/stu/yaml", func(ctx *gin.Context) {
		var stu Student
		if err := ctx.ShouldBindYAML(&stu); err != nil {
			fmt.Println(err)
			ctx.String(http.StatusBadRequest, "parse param failed")
		} else {
			ctx.String(http.StatusOK, stu.Name+" live in "+stu.Addr)
		}
	})
}

//
func UriBind(engine *gin.Engine) {
	engine.POST("/stu/uri/:name/*addr", func(ctx *gin.Context) {
		var stu Student
		if err := ctx.ShouldBindUri(&stu); err != nil {
			fmt.Println(err)
			ctx.String(http.StatusBadRequest, "parse param failed")
		} else {
			ctx.String(http.StatusOK, stu.Name+" live in "+stu.Addr)
		}
	})
}

func main() {
	gin.SetMode(gin.ReleaseMode)
	//gin.DisableConsoleColor()
	//engine := gin.Default()
	engine := gin.New()
	initlog(engine)
	engine.GET("/", getBoy)
	engine.GET("/list", getGirl)

	GetQuery(engine)
	PostForm(engine)
	RestFulForm(engine)

	UploadFile(engine)
	UploadFiles(engine)

	FormBind(engine)
	JsonBind(engine)
	XmlBind(engine)
	YamlBind(engine)
	UriBind(engine)

	if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
		v.RegisterValidation("before_today", beforeToday)
	}

	engine.Run(":8080")
}
